package com.yanggeng.demo.beginnerguidedemo.widget;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;

import com.yanggeng.demo.beginnerguidedemo.R;
import com.yanggeng.demo.beginnerguidedemo.bean.Coordinate;
import com.yanggeng.demo.beginnerguidedemo.bean.MileageStatisticsBean;
import com.yanggeng.demo.beginnerguidedemo.utils.DeviceParams;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * 可滑动的折线图
 *
 * @user: yanggeng
 * @date: 2017/12/13 10:56
 */

public class ScrollLineChartView extends View {

    private int lineColor = Color.WHITE;

    private float lineWidth;

    private float height, width;

    private int dotColor = Color.WHITE;

    private float dotRadius; // 坐标点半径

    private int axisTextColor = Color.WHITE;

    private int backgroundColor = Color.parseColor("#2FAFFF");

    private float axisTextSize;

    private int textColor = Color.WHITE;

    private int selectAxisTextColor = Color.WHITE;

    private float textSize;

    private double maxValue; //数据最大值

    private float chartHeight; //折线图高度

    private float chartWidth; // 折线图宽度

    private float cellWidth;

    //x轴偏移量
    private float xOri = 80;
    //y轴的原点坐标
    private int yOri;
    //第一个点X的坐标
    private float xInit;
    //第一个点对应的最大X坐标
    private float maxXInit;
    //第一个点对应的最小X坐标
    private float minXInit;

    private float yTopHeight;

    private int bgLineColor = Color.parseColor("#5a6b96");

    private float bgLineWidth;

    private Paint linePaint;

    private Paint axisTextPaint;

    private Paint dotPaint;

    private Paint selectPaint;

    private Paint bgLinePaint;

    private Path linePath;

    private int maxVisibleValueCount = 5;

    private List<MileageStatisticsBean> data = new ArrayList<>();

    private List<String> xAxisList = new ArrayList<>();

    private List<Coordinate> dots = new ArrayList<>(); // 坐标点集合

    private OnItemClickListener onItemClickListener;

    private boolean isInit = true;

    private float bottomPadding = 30f;

    //速度检测器
    private VelocityTracker velocityTracker;

    private Scroller mScroller;

    public ScrollLineChartView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollLineChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
        mScroller = new Scroller(context);
        axisTextSize = DeviceParams.sp2px(context, 13);
        textSize = DeviceParams.sp2px(context, 12);
        bgLineWidth = 1;

        linePaint = new Paint();
        linePaint.setColor(lineColor);
        linePaint.setStrokeWidth(lineWidth);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeCap(Paint.Cap.ROUND);
        linePaint.setStyle(Paint.Style.STROKE);

        axisTextPaint = new Paint();
        axisTextPaint.setColor(axisTextColor);
        axisTextPaint.setTextSize(axisTextSize);
        axisTextPaint.setTextAlign(Paint.Align.CENTER);
        axisTextPaint.setAntiAlias(true);
        axisTextPaint.setStrokeCap(Paint.Cap.ROUND);

        dotPaint = new Paint();
        dotPaint.setColor(dotColor);
        dotPaint.setStyle(Paint.Style.FILL);
        dotPaint.setAntiAlias(true);
        dotPaint.setStrokeCap(Paint.Cap.ROUND);

        selectPaint = new Paint();
        selectPaint.setColor(selectAxisTextColor);
        selectPaint.setTextSize(textSize);
        selectPaint.setTextAlign(Paint.Align.CENTER);
        selectPaint.setAntiAlias(true);
        selectPaint.setStrokeCap(Paint.Cap.ROUND);

        bgLinePaint = new Paint();
        bgLinePaint.setColor(bgLineColor);
        bgLinePaint.setStrokeWidth(bgLineWidth);
        bgLinePaint.setStyle(Paint.Style.STROKE);
        bgLinePaint.setAntiAlias(true);
        bgLinePaint.setStrokeCap(Paint.Cap.ROUND);

        linePath = new Path();

    }

    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.c_ScrollLineChartView, defStyleAttr, 0);

        int n = typedArray.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.c_ScrollLineChartView_lineColor:
                    lineColor = typedArray.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.c_ScrollLineChartView_lineWidth:
                    lineWidth = typedArray.getDimensionPixelSize(attr, DeviceParams.dip2px(context, 1.5f));
                    break;
                case R.styleable.c_ScrollLineChartView_dotColor2:
                    dotColor = typedArray.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.c_ScrollLineChartView_dotRadius2:
                    dotRadius = typedArray.getDimensionPixelSize(attr, DeviceParams.dip2px(context, 3));
                    break;
                case R.styleable.c_ScrollLineChartView_backgroundColor:
                    backgroundColor = typedArray.getColor(attr, Color.BLUE);
                    break;
                case R.styleable.c_ScrollLineChartView_normalTextColor:
                    axisTextColor = typedArray.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.c_ScrollLineChartView_selectedTextColor:
                    selectAxisTextColor = typedArray.getColor(attr, Color.WHITE);
                    break;
            }
        }
        typedArray.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBackground(canvas);
        if (data.size() > 0) {
            drawLineAndDot(canvas);
            drawSelectItem(canvas);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void drawBackground(Canvas canvas) {
        Paint paint = new Paint();
        paint.setColor(backgroundColor);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        if (data.size() < maxVisibleValueCount) {
            canvas.drawRect(0, 0, width, height, paint);
        } else {
            canvas.drawRect(xInit - cellWidth * data.size(), 0, width, height, paint);
        }
        Path path = new Path();
        path.moveTo(0, height - yTopHeight);
        path.lineTo(width, height - yTopHeight);
        canvas.drawPath(path, bgLinePaint);
    }

    private void drawLineAndDot(Canvas canvas) {
        maxValue = getMax(data);
        float x = xInit;
        float y;
        linePath.reset();
        linePath.moveTo(x, 0);
        // 从左往右画
//        for (int i = 0; i < data.size(); i++) {
//            String date = data.get(i).getDate();
//            float value = Float.valueOf(data.get(i).getAmount());
//            x = xInit + cellWidth * i;
//            y = yTopHeight * 2 + (1 - value / maxValue) * chartHeight;
//            canvas.drawText(date, x, yTopHeight * 2 - 10, axisTextPaint); // 横坐标日期
//            canvas.drawLine(x, yTopHeight * 2, x, height - yTopHeight, bgLinePaint); // 竖线
//            canvas.drawCircle(x, y, dotRadius, dotPaint); // 坐标点
//            if (i == 0) {
//                linePath.moveTo(x, y);
//            } else {
//                linePath.lineTo(x, y);
//            }
//            Log.e("scrollLineChartView", "x = " + x + ", y = " + y);
//        }
        // 从右往左画
        dots.clear();
        for (int i = 0; i < data.size(); i++) {
            String date = data.get(i).getTime();
            double value = data.get(i).getMiles();
            x = xInit - cellWidth * i;
            canvas.drawText(date, x, height - 25, axisTextPaint);
            canvas.drawLine(x, yTopHeight, x, height - yTopHeight, bgLinePaint);
            float yNext = yTopHeight + chartHeight;
            float xNext = xInit - cellWidth * (i + 1);
            if (maxValue == 0) {
                y = yTopHeight + chartHeight;
                yNext = yTopHeight + chartHeight;
            } else {
                y = yTopHeight + (float) (1 - value / maxValue) * chartHeight;
                if (i < data.size() - 1) {
                    yNext = yTopHeight + (float) (1 - data.get(i + 1).getMiles() / maxValue) * chartHeight;
                }
            }
            canvas.drawCircle(x, y, dotRadius, dotPaint);
            if (i < data.size() - 1) {
                canvas.drawLine(x, y, xNext, yNext, linePaint);
            }
//            if (i == 0) {
//                linePath.moveTo(x, y);
//            } else {
//                linePath.lineTo(x, y);
//            }
            dots.add(new Coordinate(x, y));
//            Log.e("scrollLineChartView", "x = " + x + ", y = " + y);
        }

        if (isInit) {
            selectX = dots.get(0).x;
            selectY = dots.get(0).y;
            selectData = data.get(0);
            if (onItemClickListener != null) {
                onItemClickListener.onItemClick(0);
            }
            isInit = false;
        }
//        linePath.close();
//        canvas.drawPath(linePath, linePaint);

    }

    private void drawSelectItem(Canvas canvas) {
        if (selectX > 0 && selectData != null) {
            canvas.drawText(selectData.getMiles() + "", selectX, selectY - 20 - dotRadius, selectPaint);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed) {
            width = getWidth();
            height = getHeight();
            initLength();
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    private void initLength() {
        yTopHeight = getTextBounds("2017", axisTextPaint).height() + 50;
        chartHeight = height - yTopHeight * 2;
        yOri = (int) (height - yTopHeight);
        for (int i = 0; i < data.size(); i++) {
            float temp = getTextBounds(data.get(i).getTime(), axisTextPaint).width() / 2;
            if (temp > xOri) {
                xOri = temp;
            }
        }
        chartWidth = width - xOri * 2;
        cellWidth = chartWidth / (maxVisibleValueCount - 1);
        xInit = width - xOri; // 从右往左画的起始横坐标
//            xInit = xOri; // 从左往右画起始横坐标
        tempInt = xInit;
        minXInit = xInit; // 从右往左画的最小横坐标
        maxXInit = xOri + cellWidth * (data.size() - 1); // 从右往左画的最大横坐标
//            minXInit = width - xOri - cellWidth * (data.size() - 1); // 从左往右画最小横坐标
//            maxXInit = xInit;// 从左往右画最大横坐标
    }

    /**
     * 获取丈量文本的矩形
     *
     * @param text
     * @param paint
     * @return
     */
    private Rect getTextBounds(String text, Paint paint) {
        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);
        return rect;
    }

    public void setData(List<MileageStatisticsBean> data) {
        if (data != null) {
            this.data.clear();
            this.data.addAll(data);
            isInit = true;
            initLength();
//            scrollTo(-mScroller.getCurrX(), 0);
            invalidate();
        }
    }

    public void setMaxVisibleValueCount(int maxVisibleValueCount) {
        this.maxVisibleValueCount = maxVisibleValueCount;
        initLength();
        isInit = true;
        invalidate();
    }

    public void clear() {
        isInit = true;
        this.data.clear();
        invalidate();
    }

    public void setSelectItem(int position) {
        if (position < dots.size()) {
            selectX = dots.get(position).x;
            selectY = dots.get(position).y;
            selectData = data.get(position);
            invalidate();
        }
    }


    private float getMax(Collection<Float> values) {
        Iterator<Float> iterator = values.iterator();
        float max = iterator.next();
        while (iterator.hasNext()) {
            float temp = iterator.next();
            if (temp > max) {
                max = temp;
            }
        }
        return max;
    }

    private double getMax(List<MileageStatisticsBean> values) {
        double max = values.get(0).getMiles();
        for (MileageStatisticsBean temp : values) {
            double tempFloat = temp.getMiles();
            if (tempFloat > max) {
                max = tempFloat;
            }
        }
        return max;
    }

    private float oldX = -1;
    private float oldY = -1;
    private float selectX = -1;
    private float selectY = -1;
    private MileageStatisticsBean selectData;
    private boolean isScrolling = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean resume = false;
        this.getParent().requestDisallowInterceptTouchEvent(true);
        obtainVelocityTracker(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                oldX = event.getX();
                oldY = event.getY();
                if (selectX != -1 || selectY != -1) {
                    selectX = -1;
                    selectY = -1;
                    invalidate();
                }
                if (data.size() > 0 && dots.size() > 0) {
                    for (int i = 0; i < dots.size(); i++) {
                        Coordinate coordinate = dots.get(i);
                        if (event.getX() > coordinate.x - cellWidth / 3 && event.getX() < coordinate.x + cellWidth / 3) {
                            selectX = coordinate.x;
                            selectY = coordinate.y;
                            selectData = data.get(i);
                            invalidate();
                            if (onItemClickListener != null) {
                                onItemClickListener.onItemClick(i);
                            }
                            resume = true;
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (selectX != -1 || selectY != -1) {
                    selectX = -1;
                    selectY = -1;
                }
                if (Math.abs(getX() - oldX) - Math.abs(getY() - oldY) > 0) {
                    if (cellWidth * data.size() > chartWidth) {
                        float x = event.getX();
                        float deltaX = x - oldX;
                        if (xInit + deltaX < minXInit) {
                            xInit = minXInit;
                        } else if (xInit + deltaX > maxXInit) {
                            xInit = maxXInit;
                        } else {
                            xInit = xInit + deltaX;
                        }
                        oldX = x;
//                        if (getScrollX() - deltaX < minXInit) {
//                            scrollBy(0, 0);
//                        } else if (getScrollX() - deltaX > maxXInit) {
//                            scrollBy(0, 0);
//                        } else {
////                            mScroller.startScroll((int)x, 0, (int)deltaX, 0, 1000);
//                            scrollBy(-(int)deltaX, 0);
//                        }
//
//                        oldX = x;
                        invalidate();
                    }
                    resume = true;
                } else {
                    resume = false;
                }
                break;
            case MotionEvent.ACTION_UP:
//                mScroller.fling((int)event.getX(), 0, (int)getVelocity(), 0, 0, 1200, 0, 0);
                scrollAfterActionUp();
                this.getParent().requestDisallowInterceptTouchEvent(false);
                recycleVelocityTracker();
                break;
            case MotionEvent.ACTION_CANCEL:
                this.getParent().requestDisallowInterceptTouchEvent(false);
                recycleVelocityTracker();
                break;
        }
        return resume;
//        return super.onTouchEvent(event);
    }

//    @Override
//    public void computeScroll() {
//        if (mScroller.computeScrollOffset()) {
////            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//            invalidate();
//        }
//    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
//        invalidate();
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public interface OnItemClickListener {
        public void onItemClick(int position);
    }

    private float tempInt;
    /**
     * 手指抬起后的滑动处理
     */
    private void scrollAfterActionUp() {
        final float velocity = getVelocity();
        Log.e("scrollLineChartView", "speed = " + velocity);
        if (Math.abs(velocity) < 512) {
            return;
        }
        float scrollLength = maxXInit - minXInit;
        Log.e("scrollLineChartView", "normal scrollLength = " + scrollLength);
        if (Math.abs(velocity) < scrollLength) {//10000是一个速度临界值，如果速度达到10000，最大可以滑动(maxXInit - minXInit)
            scrollLength = Math.abs(velocity) / 5;
//            scrollLength = (maxXInit - minXInit) * Math.abs(velocity) / 150000;
        } else {
            if (data.size() > 15) {
                scrollLength = scrollLength / 5;
            }
        }
        long mills = 1000;
        Log.e("scrollLineChartView", "current scrollLength = " + scrollLength);
        ValueAnimator animator = ValueAnimator.ofFloat(scrollLength);
        if (data.size() > 15) {
            animator.setDuration((long)(Math.abs(velocity) / (maxXInit - minXInit) * mills));
        } else {
            animator.setDuration((long) (scrollLength / (maxXInit - minXInit) * mills));//时间最大为1000毫秒，此处使用比例进行换算 (long) (scrollLength / (maxXInit - minXInit) * 3000)
        }
        tempInt = xInit;
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float value = (float) valueAnimator.getAnimatedValue();
                if (velocity < 0 && xInit > minXInit) {//向左滑动
                    if (xInit - value <= minXInit)
                        xInit = minXInit;
                    else
                        xInit = tempInt - value;
                } else if (velocity > 0 && xInit < maxXInit) {//向右滑动
                    if (xInit + value >= maxXInit)
                        xInit = maxXInit;
                    else
                        xInit = tempInt + value;
                }
                Log.e("scrollLineChartView", "xInt = " + xInit + ", value = " + value);
                invalidate();
//                tempInt = xInit;
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                isScrolling = true;
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                isScrolling = false;
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                isScrolling = false;
            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        animator.start();

    }

    /**
     * 获取速度
     * @return
     */
    private float getVelocity() {
        if (velocityTracker != null) {
            velocityTracker.computeCurrentVelocity(1000);
            return velocityTracker.getXVelocity();
        }
        return 0f;
    }

    /**
     * 获取速度跟踪器
     *
     * @param event
     */
    private void obtainVelocityTracker(MotionEvent event) {
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);
    }

    /**
     * 回收速度跟踪器
     */
    private void recycleVelocityTracker() {
        if (velocityTracker != null) {
            velocityTracker.recycle();
            velocityTracker = null;
        }
    }
}
